import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 多源API协调管理器
 * 负责协调多个天气API源，提供智能故障切换、数据聚合和性能优化
 */
public class MultiAPIManager {
    
    public enum AggregationStrategy {
        PRIMARY_FAILOVER,    // 主源优先，故障时切换
        PARALLEL_AGGREGATE,  // 并行获取，数据聚合
        BEST_PERFORMANCE,    // 选择性能最佳的源
        USER_SELECTED       // 用户手动选择的源
    }
    
    private final List<WeatherAPIService> apiServices;
    private final WeatherDataCache cache;
    private final ExecutorService executorService;
    private final UserPreferences userPreferences;
    private AggregationStrategy currentStrategy;
    private String preferredSourceId;
    private volatile boolean isShuttingDown = false;
    
    // 配置常量
    private static final int MAX_CONCURRENT_REQUESTS = 3;
    private static final int REQUEST_TIMEOUT_SECONDS = 8;
    private static final int CACHE_CLEANUP_INTERVAL_MINUTES = 30;
    
    public MultiAPIManager(UserPreferences userPreferences) {
        this.userPreferences = userPreferences;
        this.apiServices = new ArrayList<>();
        this.cache = new WeatherDataCache();
        this.executorService = Executors.newFixedThreadPool(MAX_CONCURRENT_REQUESTS);
        this.currentStrategy = AggregationStrategy.PRIMARY_FAILOVER;
        this.preferredSourceId = "openweather"; // 默认首选源
        
        // 定期清理缓存
        scheduleCacheCleanup();
        
        System.out.println("MultiAPIManager initialized with strategy: " + currentStrategy);
    }
    
    /**
     * 注册API服务
     */
    public void registerAPIService(WeatherAPIService service) {
        if (service != null && !isServiceRegistered(service.getSourceId())) {
            apiServices.add(service);
            // 按优先级排序
            apiServices.sort(Comparator.comparingInt(WeatherAPIService::getPriority));
            System.out.println("Registered API service: " + service.getDisplayName());
        }
    }
    
    /**
     * 获取当前天气数据（支持多种聚合策略）
     */
    public WeatherData getCurrentWeather(Location location) {
        String locationKey = generateLocationKey(location);
        
        try {
            switch (currentStrategy) {
                case PRIMARY_FAILOVER:
                    return getCurrentWeatherWithFailover(location, locationKey);
                    
                case PARALLEL_AGGREGATE:
                    return getCurrentWeatherWithAggregation(location, locationKey);
                    
                case BEST_PERFORMANCE:
                    return getCurrentWeatherFromBestSource(location, locationKey);
                    
                case USER_SELECTED:
                    return getCurrentWeatherFromUserSource(location, locationKey);
                    
                default:
                    return getCurrentWeatherWithFailover(location, locationKey);
            }
        } catch (Exception e) {
            System.err.println("Error getting current weather: " + e.getMessage());
            return getCachedFallback(locationKey);
        }
    }
    
    /**
     * 获取天气预报数据
     */
    public WeatherData[] getForecast(Location location, int days) {
        String locationKey = generateLocationKey(location);
        
        try {
            switch (currentStrategy) {
                case PRIMARY_FAILOVER:
                    return getForecastWithFailover(location, locationKey, days);
                    
                case PARALLEL_AGGREGATE:
                    return getForecastWithAggregation(location, locationKey, days);
                    
                case BEST_PERFORMANCE:
                    return getForecastFromBestSource(location, locationKey, days);
                    
                case USER_SELECTED:
                    return getForecastFromUserSource(location, locationKey, days);
                    
                default:
                    return getForecastWithFailover(location, locationKey, days);
            }
        } catch (Exception e) {
            System.err.println("Error getting forecast: " + e.getMessage());
            return getCachedForecastFallback(locationKey);
        }
    }
    
    /**
     * 主源优先故障切换模式 - 当前天气
     */
    private WeatherData getCurrentWeatherWithFailover(Location location, String locationKey) {
        List<WeatherAPIService> sortedServices = getSortedServicesByReliability();
        
        for (WeatherAPIService service : sortedServices) {
            try {
                // 首先检查缓存
                WeatherDataCache.CachedWeatherData cached = 
                    cache.getCachedCurrentWeather(locationKey, service.getSourceId());
                if (cached != null) {
                    System.out.println("Using cached data from " + service.getSourceId());
                    return cached.getData();
                }
                
                // 获取新数据
                long startTime = System.currentTimeMillis();
                WeatherData data = service.getCurrentWeather(location);
                long latency = System.currentTimeMillis() - startTime;
                
                if (data != null) {
                    cache.cacheCurrentWeather(locationKey, data, service.getSourceId(), latency);
                    System.out.println("Got current weather from " + service.getSourceId() + 
                                     " (latency: " + latency + "ms)");
                    return data;
                }
                
            } catch (Exception e) {
                cache.recordSourceFailure(service.getSourceId());
                System.err.println("Failed to get weather from " + service.getSourceId() + ": " + e.getMessage());
                // 继续尝试下一个源
            }
        }
        
        throw new RuntimeException("All API sources failed to provide current weather data");
    }
    
    /**
     * 并行聚合模式 - 当前天气
     */
    private WeatherData getCurrentWeatherWithAggregation(Location location, String locationKey) {
        List<Future<WeatherDataCache.CachedWeatherData>> futures = new ArrayList<>();
        
        // 并发获取所有可用源的数据
        for (WeatherAPIService service : getAvailableServices()) {
            Future<WeatherDataCache.CachedWeatherData> future = executorService.submit(() -> {
                try {
                    // 先检查缓存
                    WeatherDataCache.CachedWeatherData cached = 
                        cache.getCachedCurrentWeather(locationKey, service.getSourceId());
                    if (cached != null) {
                        return cached;
                    }
                    
                    // 获取新数据
                    long startTime = System.currentTimeMillis();
                    WeatherData data = service.getCurrentWeather(location);
                    long latency = System.currentTimeMillis() - startTime;
                    
                    if (data != null) {
                        cache.cacheCurrentWeather(locationKey, data, service.getSourceId(), latency);
                        return new WeatherDataCache.CachedWeatherData(data, service.getSourceId(), latency);
                    }
                    
                } catch (Exception e) {
                    cache.recordSourceFailure(service.getSourceId());
                    System.err.println("Service " + service.getSourceId() + " failed: " + e.getMessage());
                }
                return null;
            });
            
            futures.add(future);
        }
        
        // 收集结果
        List<WeatherDataCache.CachedWeatherData> results = new ArrayList<>();
        for (Future<WeatherDataCache.CachedWeatherData> future : futures) {
            try {
                WeatherDataCache.CachedWeatherData result = 
                    future.get(REQUEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
                if (result != null) {
                    results.add(result);
                }
            } catch (TimeoutException | InterruptedException | ExecutionException e) {
                System.err.println("Request timeout or interrupted: " + e.getMessage());
            }
        }
        
        if (results.isEmpty()) {
            throw new RuntimeException("No API sources provided valid data");
        }
        
        // 聚合数据
        WeatherData aggregated = cache.aggregateCurrentWeatherData(results);
        System.out.println("Aggregated data from " + results.size() + " sources");
        return aggregated;
    }
    
    /**
     * 最佳性能源模式 - 当前天气
     */
    private WeatherData getCurrentWeatherFromBestSource(Location location, String locationKey) {
        WeatherAPIService bestService = getBestPerformanceService();
        
        if (bestService != null) {
            try {
                // 检查缓存
                WeatherDataCache.CachedWeatherData cached = 
                    cache.getCachedCurrentWeather(locationKey, bestService.getSourceId());
                if (cached != null) {
                    return cached.getData();
                }
                
                // 获取新数据
                long startTime = System.currentTimeMillis();
                WeatherData data = bestService.getCurrentWeather(location);
                long latency = System.currentTimeMillis() - startTime;
                
                if (data != null) {
                    cache.cacheCurrentWeather(locationKey, data, bestService.getSourceId(), latency);
                    return data;
                }
                
            } catch (Exception e) {
                cache.recordSourceFailure(bestService.getSourceId());
                System.err.println("Best source failed, falling back to failover mode");
                return getCurrentWeatherWithFailover(location, locationKey);
            }
        }
        
        // 如果没有最佳源，回退到故障切换模式
        return getCurrentWeatherWithFailover(location, locationKey);
    }
    
    /**
     * 用户选择源模式 - 当前天气
     */
    private WeatherData getCurrentWeatherFromUserSource(Location location, String locationKey) {
        WeatherAPIService userService = getServiceById(preferredSourceId);
        
        if (userService != null) {
            try {
                // 检查缓存
                WeatherDataCache.CachedWeatherData cached = 
                    cache.getCachedCurrentWeather(locationKey, userService.getSourceId());
                if (cached != null) {
                    return cached.getData();
                }
                
                // 获取新数据
                long startTime = System.currentTimeMillis();
                WeatherData data = userService.getCurrentWeather(location);
                long latency = System.currentTimeMillis() - startTime;
                
                if (data != null) {
                    cache.cacheCurrentWeather(locationKey, data, userService.getSourceId(), latency);
                    return data;
                }
                
            } catch (Exception e) {
                cache.recordSourceFailure(userService.getSourceId());
                System.err.println("User preferred source failed, falling back to failover mode");
                return getCurrentWeatherWithFailover(location, locationKey);
            }
        }
        
        // 如果用户选择的源不可用，回退到故障切换模式
        return getCurrentWeatherWithFailover(location, locationKey);
    }
    
    /**
     * 预报数据故障切换模式
     */
    private WeatherData[] getForecastWithFailover(Location location, String locationKey, int days) {
        List<WeatherAPIService> sortedServices = getSortedServicesByReliability();
        
        for (WeatherAPIService service : sortedServices) {
            try {
                // 检查缓存
                WeatherDataCache.CachedWeatherData[] cached = 
                    cache.getCachedForecast(locationKey, service.getSourceId());
                if (cached != null) {
                    WeatherData[] forecast = new WeatherData[cached.length];
                    for (int i = 0; i < cached.length; i++) {
                        forecast[i] = cached[i].getData();
                    }
                    System.out.println("Using cached forecast from " + service.getSourceId());
                    return forecast;
                }
                
                // 获取新数据
                long startTime = System.currentTimeMillis();
                WeatherData[] forecast = service.getForecast(location, days);
                long latency = System.currentTimeMillis() - startTime;
                
                if (forecast != null && forecast.length > 0) {
                    cache.cacheForecast(locationKey, forecast, service.getSourceId(), latency);
                    System.out.println("Got forecast from " + service.getSourceId() + 
                                     " (latency: " + latency + "ms)");
                    return forecast;
                }
                
            } catch (Exception e) {
                cache.recordSourceFailure(service.getSourceId());
                System.err.println("Failed to get forecast from " + service.getSourceId() + ": " + e.getMessage());
            }
        }
        
        throw new RuntimeException("All API sources failed to provide forecast data");
    }
    
    /**
     * 预报数据聚合模式（简化版，主要使用最可靠源）
     */
    private WeatherData[] getForecastWithAggregation(Location location, String locationKey, int days) {
        // 预报聚合比较复杂，这里使用最可靠源的数据
        WeatherAPIService bestService = getMostReliableService();
        if (bestService != null) {
            try {
                long startTime = System.currentTimeMillis();
                WeatherData[] forecast = bestService.getForecast(location, days);
                long latency = System.currentTimeMillis() - startTime;
                
                if (forecast != null) {
                    cache.cacheForecast(locationKey, forecast, bestService.getSourceId(), latency);
                    return forecast;
                }
            } catch (Exception e) {
                cache.recordSourceFailure(bestService.getSourceId());
            }
        }
        
        // 回退到故障切换模式
        return getForecastWithFailover(location, locationKey, days);
    }
    
    /**
     * 最佳性能源预报
     */
    private WeatherData[] getForecastFromBestSource(Location location, String locationKey, int days) {
        WeatherAPIService bestService = getBestPerformanceService();
        
        if (bestService != null) {
            try {
                long startTime = System.currentTimeMillis();
                WeatherData[] forecast = bestService.getForecast(location, days);
                long latency = System.currentTimeMillis() - startTime;
                
                if (forecast != null) {
                    cache.cacheForecast(locationKey, forecast, bestService.getSourceId(), latency);
                    return forecast;
                }
            } catch (Exception e) {
                cache.recordSourceFailure(bestService.getSourceId());
            }
        }
        
        return getForecastWithFailover(location, locationKey, days);
    }
    
    /**
     * 用户选择源预报
     */
    private WeatherData[] getForecastFromUserSource(Location location, String locationKey, int days) {
        WeatherAPIService userService = getServiceById(preferredSourceId);
        
        if (userService != null) {
            try {
                long startTime = System.currentTimeMillis();
                WeatherData[] forecast = userService.getForecast(location, days);
                long latency = System.currentTimeMillis() - startTime;
                
                if (forecast != null) {
                    cache.cacheForecast(locationKey, forecast, userService.getSourceId(), latency);
                    return forecast;
                }
            } catch (Exception e) {
                cache.recordSourceFailure(userService.getSourceId());
            }
        }
        
        return getForecastWithFailover(location, locationKey, days);
    }
    
    // 辅助方法
    
    private List<WeatherAPIService> getSortedServicesByReliability() {
        return apiServices.stream()
            .filter(WeatherAPIService::isServiceAvailable)
            .sorted((a, b) -> {
                double scoreA = cache.getSourceReliabilityScore(a.getSourceId());
                double scoreB = cache.getSourceReliabilityScore(b.getSourceId());
                return Double.compare(scoreB, scoreA); // 降序
            })
            .collect(Collectors.toList());
    }
    
    private List<WeatherAPIService> getAvailableServices() {
        return apiServices.stream()
            .filter(WeatherAPIService::isServiceAvailable)
            .collect(Collectors.toList());
    }
    
    private WeatherAPIService getBestPerformanceService() {
        return apiServices.stream()
            .filter(WeatherAPIService::isServiceAvailable)
            .min((a, b) -> {
                WeatherDataCache.SourceReliabilityStats statsA = cache.getSourceStats(a.getSourceId());
                WeatherDataCache.SourceReliabilityStats statsB = cache.getSourceStats(b.getSourceId());
                
                double latencyA = statsA != null ? statsA.getAverageLatency() : 5000.0;
                double latencyB = statsB != null ? statsB.getAverageLatency() : 5000.0;
                
                return Double.compare(latencyA, latencyB);
            })
            .orElse(null);
    }
    
    private WeatherAPIService getMostReliableService() {
        return apiServices.stream()
            .filter(WeatherAPIService::isServiceAvailable)
            .max((a, b) -> {
                double scoreA = cache.getSourceReliabilityScore(a.getSourceId());
                double scoreB = cache.getSourceReliabilityScore(b.getSourceId());
                return Double.compare(scoreA, scoreB);
            })
            .orElse(null);
    }
    
    private WeatherAPIService getServiceById(String sourceId) {
        return apiServices.stream()
            .filter(service -> service.getSourceId().equals(sourceId))
            .findFirst()
            .orElse(null);
    }
    
    private boolean isServiceRegistered(String sourceId) {
        return apiServices.stream()
            .anyMatch(service -> service.getSourceId().equals(sourceId));
    }
    
    private String generateLocationKey(Location location) {
        return location.getCity().toLowerCase().replaceAll("\\s+", "_");
    }
    
    private WeatherData getCachedFallback(String locationKey) {
        // 尝试从任何缓存源获取数据
        for (WeatherAPIService service : apiServices) {
            WeatherDataCache.CachedWeatherData cached = 
                cache.getCachedCurrentWeather(locationKey, service.getSourceId());
            if (cached != null) {
                System.out.println("Using fallback cached data from " + service.getSourceId());
                return cached.getData();
            }
        }
        return null;
    }
    
    private WeatherData[] getCachedForecastFallback(String locationKey) {
        // 尝试从任何缓存源获取预报数据
        for (WeatherAPIService service : apiServices) {
            WeatherDataCache.CachedWeatherData[] cached = 
                cache.getCachedForecast(locationKey, service.getSourceId());
            if (cached != null) {
                WeatherData[] forecast = new WeatherData[cached.length];
                for (int i = 0; i < cached.length; i++) {
                    forecast[i] = cached[i].getData();
                }
                System.out.println("Using fallback cached forecast from " + service.getSourceId());
                return forecast;
            }
        }
        return null;
    }
    
    private void scheduleCacheCleanup() {
        ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor();
        cleanupScheduler.scheduleWithFixedDelay(
            cache::cleanupExpiredCache,
            CACHE_CLEANUP_INTERVAL_MINUTES,
            CACHE_CLEANUP_INTERVAL_MINUTES,
            TimeUnit.MINUTES
        );
    }
    
    // 公共配置方法
    
    public void setAggregationStrategy(AggregationStrategy strategy) {
        this.currentStrategy = strategy;
        System.out.println("Aggregation strategy changed to: " + strategy);
    }
    
    public AggregationStrategy getAggregationStrategy() {
        return currentStrategy;
    }
    
    public void setPreferredSource(String sourceId) {
        this.preferredSourceId = sourceId;
        System.out.println("Preferred source set to: " + sourceId);
    }
    
    public String getPreferredSource() {
        return preferredSourceId;
    }
    
    public List<WeatherAPIService> getRegisteredServices() {
        return new ArrayList<>(apiServices);
    }
    
    public WeatherDataCache getCache() {
        return cache;
    }
    
    public void shutdown() {
        isShuttingDown = true;
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
        }
        System.out.println("MultiAPIManager shutdown completed");
    }
} 